#include "noeud.hpp"

void initNoeud (noeud &n, unsigned int nb_fils, bool verbose)
{
    if (verbose)
        cout << "\tentrée dans initNoeud" << endl;
    n.capaFils = nb_fils;
    n.nbFils = 0;
    if(verbose)
        cout << "\tinformations copiées" << endl;

    if (nb_fils != 0)
    {
        n.fils = (noeud**) malloc(sizeof(noeud*)*nb_fils);
        if (verbose)
            cout << "\tnb de fils : " << nb_fils << " / tableau de fils reservé" << endl;
    } else {
        n.fils = NULL;
        if (verbose)
            cout << "\tpas de tableau de fils réservé" << endl;
    }
}

void initNoeud(noeud& n, const noeud& copie, bool verbose)
{
    if (verbose)
        cout << "entrée dans l'initialisation de noeud par copie" << endl;

    if (verbose)
        affichageNoeudComplet(copie);

    if(copie.type == t_valeur)
    {
        if(verbose)
            cout << "\tcopie d'une valeur" << endl;
        initValeur(n, copie.info.valeur, verbose);
    } else {
        if (verbose)
            cout << "\tcopie d'un opérateur" << endl;

        initOperateur(n, copie.info.op, copie.capaFils, verbose);

        if (verbose)
            cout << "\tcopie des fils (il y a " << copie.nbFils << " fils) :" << endl;
        /* copie des fils */
        for(unsigned int i = 0; i<copie.nbFils; i++)
        {
            if (verbose)
                cout << "\tcopie du fils " << i << endl;

            noeud* fils = (noeud*) malloc(sizeof(noeud));
            initNoeud(*fils, *(copie.fils[i]), verbose);
            ajoutFils(n, fils, verbose);
        }
    }
}

void initNoeud(noeud& n, string expression, bool verbose)
{
    if(verbose)
    {
        cout << "entrée dans l'initialisation de noeud par expression" << endl;
        cout << "l'expression en cours est " << expression << endl;
    }
    if(!expression.empty())
    {
        string::iterator it = expression.begin(); // le premier élément à analyser est forcément à la permière place
        switch (*it) {
            case '+' : {
                if (verbose)
                    cout << "operateur trouvé : +" << endl;

                initOperateur(n, o_plus, 2, verbose);
                size_t position = expression.find('(');
                size_t posFinPar; // position des parenthéses fermantes
                while (position != string::npos)
                {
                    posFinPar = finParenthese(expression.substr(position))+position;
                    if(verbose)
                    {
                        cout << "\tposition de la première ( : " << position << endl;;
                        cout << "\tposition de la ) associée : " << posFinPar << endl;
                    }

                    noeud* fils = (noeud*) malloc(sizeof(noeud));
                    initNoeud(*fils, expression.substr(position+1, (posFinPar-position-1)), verbose);
                    ajoutFils(n, fils, verbose);
                    position = expression.find('(', posFinPar+1);
                }
                break;
            }

            case '*' : {
                if (verbose)
                    cout << "operateur trouvé : +" << endl;

                initOperateur(n, o_mult, 2, verbose);
                size_t position = expression.find('(');
                size_t posFinPar; // position des parenthéses fermantes
                while (position != string::npos)
                {
                    posFinPar = finParenthese(expression.substr(position))+position;
                    if(verbose)
                    {
                        cout << "\tposition de la première ( : " << position << endl;;
                        cout << "\tposition de la ) associée : " << posFinPar << endl;
                    }

                    noeud* fils = (noeud*) malloc(sizeof(noeud));
                    initNoeud(*fils, expression.substr(position+1, (posFinPar-position-1)), verbose);
                    ajoutFils(n, fils, verbose);
                    position = expression.find('(', posFinPar+1);
                }
                break;
            }
            default : {// c'est un nombre
                if (verbose)
                    cout << "nombre trouvé : "  << atoi(expression.c_str()) << endl;
                initValeur(n, atoi(expression.c_str()), verbose);
                break;
            }
        }
    }
}

void initValeur(noeud &n, int i, bool verbose) {
    if (verbose)
        cout << "entrée dans initValeur" << endl;

    initNoeud(n, 0, verbose);

    if (verbose)
        cout << "sortie de initNoeud" << endl;

    n.info.valeur = i;
    n.type = t_valeur;
}

void initOperateur(noeud& n, operateur op, unsigned int nb_fils, bool verbose) {
    initNoeud(n, nb_fils, verbose);
    n.info.op = op;
    n.type = t_operateur;
}

void ajoutFils(noeud& parent, noeud* fils, bool verbose)
{
    if (verbose)
        cout << "entrée dans ajoutFils" << endl;
    if (parent.capaFils == parent.nbFils)
    {
        if (verbose)
            cout << "\trealloc necessaire" << endl;

        if(parent.capaFils == 0)
        {
            parent.capaFils = 1;
        } else {
            parent.capaFils *= 2;
        }
        parent.fils = (noeud**) realloc(parent.fils, sizeof(noeud*)*parent.capaFils);

        if (verbose)
            cout << "\trealloc terminée" << endl;
    }

    if (verbose)
        cout << "\tajout du fils à la case " << parent.nbFils << endl;

    parent.fils[parent.nbFils] = fils;

    if (verbose)
        cout << "\tfils ajouté" << endl;

    parent.nbFils ++;
/*
    if(parent.nbFils > 20)
        exit(0); */
}

void ajoutFils(noeud& parent, elt info, e_type type, unsigned int nb_fils, bool verbose)
{
    noeud* fils = (noeud*) malloc(sizeof(noeud));
    if(type == t_valeur)
    {
        initValeur(*fils, info.valeur, verbose);
    } else {
        initOperateur(*fils, info.op, nb_fils, verbose);
    }
    ajoutFils(parent, fils, verbose);
}

void testamentFils(noeud& parent, bool verbose)
{
    for (unsigned int i=0; i<parent.nbFils; i++)
    {
        testamentNoeud(*(parent.fils[i]), verbose);
    }
    parent.nbFils = 0;

    free(parent.fils);
    parent.capaFils = 0;
}

void testamentNoeud(noeud &n, bool verbose)
{
    if(!noeudFeuille(n))
    {
        testamentFils(n, verbose);
    }
    free(&n);
}

void affichageNoeudExpression(const noeud& n, bool racine)
{
    if(noeudFeuille(n))
    {
        cout << n.info.valeur;
    } else {
        if (!racine)
            cout << "(";

        for (unsigned int i=0; i<n.nbFils; i++)
        {
            affichageNoeudExpression(*(n.fils[i]));
            if(i<n.nbFils-1)
            {
                cout << " ";
                affichageOp(n.info.op);
                cout << " ";
            }
        }
        if(!racine)
            cout << ")";
    }
}

void affichageNoeudHorizontal(const noeud& n, int niveau)
{
    for(int i=0; i<niveau-1; i++)
    {
        cout << "  ";
    }
    if(niveau>0)
        cout << "|-";

    if(noeudFeuille(n))
    {
        cout << n.info.valeur << endl;
    } else {
        affichageOp(n.info.op);
        cout << endl;
        for (unsigned int i=0; i<n.nbFils; i++)
        {
            affichageNoeudHorizontal(*(n.fils[i]), niveau+1);
        }
    }
}

void affichageOp(const operateur op, bool gestErr)
{
    switch (op) {
        case o_plus :
            cout << "+";
            break;
        case o_mult :
            cout << "*";
            break;
        default :
            if (gestErr)
            {
                cout << "operateur non reconnu ";
            }
            break;
    }
}

bool noeudFeuille (const noeud& n)
{
    return (n.fils == NULL && n.type == t_valeur && n.nbFils == 0);
}

bool noeudFilsFeuille (const noeud &n)
{
    if(noeudFeuille(n))
        return false;

    for(unsigned int i = 0; i<n.nbFils; i++)
    {
        if(!noeudFeuille(*(n.fils[i])))
            return false;
    }
    return true;
}

int valeurNoeud (const noeud& n, bool gestErr)
{
    if (noeudFeuille(n))
    {
        return n.info.valeur;
    }
    else
    {
        switch (n.info.op) {
            case o_plus:
            {
                int somme = 0;
                for(unsigned int i = 0; i<n.nbFils; i++)
                {
                    somme += valeurNoeud(*(n.fils[i]));
                }
                return somme;
                break;
            }

            case o_mult:
            {
                int produit = 1;
                for(unsigned int i = 0; i<n.nbFils; i++)
                {
                    produit *= valeurNoeud(*(n.fils[i]));
                }
                return produit;
                break;
            }

            default:
                if(gestErr)
                {
                    cout << "l'operateur spécifié est inconnu" << endl;
                }
                return 0;
                break;
        }
    }
}

void affichageNoeudComplet(const noeud & n)
{
    cout << "affichage complet du noeud :" << endl;
    cout << "\tadresse du noeud :" << &n << endl;
    cout << "\ttype d'info : ";
    switch (n.type){
        case t_operateur :
            cout << "operateur" << endl;
            cout << "\tinfo : ";
            affichageOp(n.info.op);
            cout << endl;
            break;
        case t_valeur :
            cout << "valeur" << endl;
            cout << "\tinfo : " << n.info.valeur << endl;
            break;
        default :
            break;
    }

    cout << "\tcapacité : " << n.capaFils << endl;
    cout << "\tnombre de fils : " << n.nbFils << endl;
    if(n.nbFils > 0)
    {
        cout << "\taffichage de l'adresse des fils :" << endl;
        for (unsigned int i = 0; i<n.nbFils; i++)
        {
            cout << "\t\t" << n.fils[i] << endl;
        }
    }
}
